package gradle.common.redis.utils;

import gradle.common.core.context.SpringApplicationContext;
import gradle.common.core.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * @author 1141193930@qq.com
 */
@Slf4j
public class RedisUtil {


    /**
     * 获取 Redis 中 String 的 Value
     *
     * @param key key
     * @return value
     */
    public static String getString(String key) {
        try {
            StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);
            return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("【System】连接异常，Error: {}", ExceptionUtils.getStackTrace(e));
            return null;
        }
    }

    /**
     * 获取 Redis 中 String 的 Value 且过滤null和{}字符串
     *
     * @param key                  key
     * @param filteringEmptyString 是否过滤空字符
     * @return value
     */
    public static String getString(String key, boolean filteringEmptyString) {
        String value = getString(key);
        if (filteringEmptyString) {
            return StringUtils.isNotBlank(value) && !"null".equals(value) && !"{}".equals(value) ? value : null;
        }
        return value;
    }

    /**
     * 设置不过期key
     *
     * @param key   key
     * @param value value
     */
    public static void setString(String key, String value) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);
        redisTemplate.opsForValue().set(key, value);
    }

    public static void setString(String key, String value, long timeout, TimeUnit timeUnit) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        try {
            if (StringUtils.isBlank(key)) {
                return;
            }
            redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
        } catch (Exception e) {
            log.error("【System】连接异常，Error: {}", ExceptionUtils.getStackTrace(e));
        }
    }

    /**
     * 如果为空就set值，并返回1；如果存在(不为空)不进行操作，并返回0。
     *
     * @param key      key
     * @param value    value
     * @param timeout  超时时间
     * @param timeUnit 超时时间单位
     * @return set成功返回1，反之返回0，若redis异常返回空
     */
    public static Boolean setNxString(String key, String value, long timeout, TimeUnit timeUnit) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        try {
            return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
        } catch (Exception e) {
            log.error("【System】连接异常，Error: {}", ExceptionUtils.getStackTrace(e));
        }
        return null;
    }

    /**
     * 判断redis中是否存在key
     *
     * @param key key
     * @return true or false
     */
    public static boolean hasKey(String key) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error("【System】连接异常，Error: {}", ExceptionUtils.getStackTrace(e));
        }
        return false;
    }

    /**
     * 删除缓存
     *
     * @param key key
     */
    public static void delete(String key) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("【System】连接异常，Error: {}", ExceptionUtils.getStackTrace(e));
        }
    }


    /**
     * 批量删除缓存
     *
     * @param keys keys
     */
    public static void delete(Set<String> keys) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        try {
            redisTemplate.delete(keys);
        } catch (Exception e) {
            log.error("【System】连接异常，Error: {}", ExceptionUtils.getStackTrace(e));
        }
    }


    /**
     * 根据Hash表的key和域Field列表获取对应的Value列表(可用于获取多个对象或者对象的多个属性)
     *
     * @param key    Hash表的key
     * @param fields 域Field列表
     * @return List<T> Hash的key和域Field列表获取对应的Value列表
     */
    public static List<Object> hashMultiGet(String key, List<String> fields) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        redisTemplate.setHashKeySerializer(new GenericToStringSerializer<>(String.class));
        return redisTemplate.opsForHash().multiGet(key, Collections.singleton(fields));
    }


    /**
     * 根据Hash表的key和域Field列表获取对应的Value列表(可用于获取多个对象或者对象的多个属性)
     *
     * @param key   Hash表的key
     * @param field 域Field
     * @return Object Hash的key和域Field列表获取对应的Value列表
     */
    public static Object hashGet(String key, String field) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * hash类型，批量put
     *
     * @param key Hash表的key
     * @param map Map<fields, title>
     */
    private static void putHashValues(String key, Map<String, String> map) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * hash类型，批量put
     *
     * @param key      Hash表的key
     * @param map      Map<fields, title>
     * @param timeout  过期数值
     * @param timeUnit 过期数值单位
     */
    public static void putHashValues(String key, Map<String, String> map, long timeout, TimeUnit timeUnit) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        putHashValues(key, map);
        redisTemplate.expire(key, timeout, timeUnit);
    }

    /**
     * 检查set中是否包含指定的值
     *
     * @param key   set的key
     * @param value 要检查的值
     * @return 如果set中包含该值，则返回true；否则返回false
     */
    public static Boolean isSetContainsValue(String key, String value) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        try {
            return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));
        } catch (Exception e) {
            log.error("【System】连接异常，Error: {}", ExceptionUtils.getStackTrace(e));
            return null;
        }
    }

    /**
     * 获取自增序列
     **/
    public static Long incr(String key, long timeout, TimeUnit timeUnit) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        try {
            // 自增
            Long counter = redisTemplate.opsForValue().increment(key);

            // 设置过期时间：如果是第一次生成，则设置 24 小时过期
            if (counter != null && counter == 1L) {
                redisTemplate.expire(key, timeout, timeUnit);
            }
            return counter;
        } catch (Exception e) {
            log.error("【System】获取自增序列异常，Error: {}", ExceptionUtils.getStackTrace(e));
            return null;
        }
    }

    /**
     * 根据前缀删除
     *
     * @param keyPrefix 前缀
     */
    public static void deleteByPrefix(String keyPrefix) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        // 防止全部删除
        if (StringUtils.isBlank(keyPrefix)) {
            return;
        }
        try {
            Set<String> keys = keys(keyPrefix + "*");
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
            }
        } catch (Exception e) {
            log.error("【System】连接异常，Error: {}", ExceptionUtils.getStackTrace(e));
        }
    }


    /**
     * 获取key
     *
     * @param pattern 前缀
     */
    public static Set<String> keys(String pattern) {
        Set<String> keys = new HashSet<>();
        scan(pattern, item -> {
            // 符合条件的key
            String key = new String(item, StandardCharsets.UTF_8);
            keys.add(key);
        });
        return keys;
    }


    /**
     * scan 实现
     *
     * @param pattern  表达式
     * @param consumer 对迭代到的key进行操作
     */
    private static void scan(String pattern, Consumer<byte[]> consumer) {
        StringRedisTemplate redisTemplate = SpringApplicationContext.getBeanIfNull(StringRedisTemplate.class);

        redisTemplate.execute((RedisConnection connection) -> {
            try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
                cursor.forEachRemaining(consumer);
                return null;
            } catch (Exception e) {
                log.error("【Redis缓存】缓存扫描异常，pattern：{}，error：{}", pattern, ExceptionUtils.getStackTrace(e));
                throw new BusinessException(e.getMessage());
            }
        });

    }

}
